昨天,我們設立了 Pull Request Flow 與 Branch protection rules
,確保任何程式碼進主分支前都得經過檢查與批准。
今天,我們要處理另一個經典問題:
「在我電腦能跑,不代表在你電腦能跑」
這句話不是藉口,而是一種古老的詛咒。它能讓任何 CI/CD pipeline 崩潰。為了打破這詛咒,我們引入兩個工具:
我們今天就是在打造一個「多重世界線的驗證機構」。如果岡部倫太郎有這工具,可能就不需要一直在各個世界線跑來跑去救真由理了。
DevOps 解決的是 持續交付 (CD) ,但 DevSecOps 還得顧慮 安全與穩定性。
多版本測試的意義不只是「程式能不能跑」,還包含:
依賴風險管理
不同 Python 版本可能帶有不同套件依賴。沒測過的話,某版本可能暗藏漏洞或錯誤。
避免版本漂移(Version Drift)
「我環境能跑、你環境卻掛掉」的情況,通常源於環境差異。多版本測試能提早踩雷。
合規要求
金融、醫療等產業常有明確規範,要求系統能在特定版本下安全執行。
👉 想像《進擊的巨人》裡的各個城牆:
你不能只在「希娜之牆」測過,卻期望「瑪利亞之牆」也能同樣安全無虞。
pip install tox
tox.ini
[tox]
envlist = py310, py311, py312
skip_missing_interpreters = true
[testenv]
deps = -r requirements.txt
commands = pytest -q
tox
會自動在 Python 3.10、3.11、3.12 上跑測試。
Windows系統如果要下載多版本Python,只要到官網(python.org)安裝加到path即可。
缺版本?它會跳過,但同時提醒你:「你的環境不完整,最好補齊」。
Q:我電腦只有一個 Python 版本,還需要 tox 嗎?
A:需要。因為 CI/CD 端會跑多版本,你最好先本地抓 bug,不然會變成 CI 一直報紅,心態會比世界線跳躍還崩潰。
tox 保護的是你的電腦世界線,但我們還要驗證「所有人的世界線」。
在 GitHub Actions 的 workflow 裡設定 Matrix Build:
.github/workflows/ci.yml
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: [3.10, 3.11, 3.12]
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }} # 這段是要指定 Python版本
- name: Install deps
run: pip install -r requirements.txt
- name: Run tests
run: pytest -q
這樣,三條「平行世界線」會同時跑測試,全部通過才算過關。
Q:有 tox 還要 Matrix Build?
A:因為tox 保護的是「你的世界線」,而Matrix Build 保護的是「所有人的世界線」。
再搭配昨天(Day.6)的 Branch Protection Rules,就能強制要求 PR 必須通過所有 Python 版本的檢查。守門人從「有跑測試」進化為「所有世界線的測試都要通過」。
做到這裡,你的流程已經符合企業級 DevSecOps pipeline 的雛形
這種設計,不只是穩定性,更是安全性的基礎。
因為一旦跨版本差異造成漏洞,攻擊者就能趁虛而入。
而我們的多世界線驗證,正是為了讓所有環境保持一致、防止破口。
在這邊整理一下目前的CI配置:
name: CI
on:
push:
branches: [ master ] # 確認你的預設分支真的是 master;若是 main 要同步改
pull_request:
branches: [ master ]
# 最小權限 + 同分支只跑最新一次
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
build:
name: Test (${{ matrix.python-version }})
runs-on: ubuntu-latest
timeout-minutes: 15 #單個 job 最多跑 15 分鐘,超時會自動中止。
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }} # 這段是要指定 Python 版本
cache: pip # 直接用內建 pip 快取(會依 requirements* 與版本分開)
- name: Install deps
run: |
python -m pip install --upgrade pip
if [ -f requirements.txt ]; then python -m pip install -r requirements.txt; fi
python -m pip install pytest flake8 black
- name: Format check (black)
run: python -m black --check .
- name: Lint code
run: python -m flake8 . --format=github # flake8 直接回饋到 PR(GitHub annotation)
- name: Run tests
run: python -m pytest -q --maxfail=1 --disable-warnings --junitxml=pytest.xml
- name: Upload test report
if: failure()
uses: actions/upload-artifact@v4
with:
name: pytest-report-${{ matrix.python-version }}
path: pytest.xml
除了之前寫的片段,還加入了一些功能提高效率和安全性。
permissions: contents: read
→ 只給最小必要權限,減少安全風險。
concurrency
→ 保證同一個分支同時間只會跑 一個 workflow,舊的直接取消,避免資源浪費。
python -m flake8 . --format=github
pytest -q --maxfail=1 --disable-warnings --junitxml=pytest.xml
q
減少輸出-maxfail=1
測一次錯誤就停-disable-warnings
關掉 warning-junitxml
產出報告檔 pytest.xml
(在 GitHub Actions 頁面的 Artifacts 區塊下載,在這邊我寫只有 測試失敗 的時候才會被存成 artifact。)今天,我們把 CI/CD 的測試邏輯拓展到「多世界線」:
這不只是為了讓開發流程更順暢,更是守住系統安全的關鍵。
就像岡部要確保所有世界線都能拯救真由理一樣,
我們要確保所有版本下,系統都能安全、穩定地存活。